var Socket = require("socket").Socket;
var Buffer = require("binary").Buffer;
var http = require("http");
var fs = require("fs");

/**
 * Class: LNHTTPRequest
 */
function LNHTTPRequest(options)
{
	this.socket=options.socket;
	this.server=options.server;
	this.app=options.app;
	this.comet=options.comet;

	this.request_headers={};
	this.reply_headers={};
	this.request_method="UNKNOWN";
	this.request_uri="";
	this.protocol_version="";
	this.errors=[];
	this.pending=false;
}

LNHTTPRequest.prototype.parse_first_line=function(buffer,start,end)
{
	var line="";
	var spaces=[];
	for (var i=start;i<end;i++) if (buffer[i]==32) spaces.push(i);
	if (spaces.length!=2) {
		this.errors.push("LNHTTPRequest.parse_first_line() - could not parse");
		system.stdout.writeLine("Could not parse, start="+start+", end="+end);
		for (var i=start;i<end;i++) system.stdout.write(buffer[i]);
		system.stdout.write("\n");
		system.stdout.writeLine("Could not parse '"+buffer.toString("ASCII",start,end)+"'");
		return;
	}
	this.request_method		=buffer.toString("ASCII",start,spaces[0]);
	this.request_uri		=buffer.toString("ASCII",spaces[0]+1,spaces[1]);
	this.protocol_version	=buffer.toString("ASCII",spaces[1]+1,end);
}

LNHTTPRequest.prototype.parse_header=function(buffer,start,end)
{
	var i=start;
	while (i<end-1 && (!(buffer[i]==0x3a && buffer[i+1]==32))) i++;
	if (i==end-1) {
		this.errors.push("LNHTTPRequest.parse_header() - no ': ' in header");
		return;
	}
	var k=buffer.toString("ASCII",start,i);
	var v=buffer.toString("ASCII",i+2,end);
	this.request_headers[k.toLowerCase()]=v;
}

LNHTTPRequest.prototype.reply=function(status_code,data)
{
	var r="HTTP/1.1 "+status_code+"\r\n";
	this.reply_headers["Server"]="Lattenoir application server";
	this.reply_headers["Allow"]="GET,POST,HEAD";
	this.reply_headers["Connection"]="close";
//	this.reply_headers["Date"]="Fri, 12 Sep 2014 06:13:40 GMT";
//	this.reply_headers["Vary"]="Accept-Encoding";
//	this.reply_headers["Content-Length"]="289";
	if (!this.reply_headers["Content-Type"]) this.reply_headers["Content-Type"]="text/html; charset=iso-8859-1";
	for (var k in this.reply_headers) {
		if (k=="Status") continue;
		r+=k+": "+this.reply_headers[k]+"\r\n";
	}
	this.socket.send(r);// data already has "\r\n";
	this.socket.send(data);
/*	var f=new fs.File("/tmp/lns/last");
	try {
		f.open("w");
		f.write("INPUT ====================\n");
		f.write("OUTPUT ===================\n");
		f.write(r+"\r\n");
		f.write("DATA =====================\n");
		f.write(data);
		f.close();
	} catch(e) {
		system.stdout.writeLine(e);
	}*/
	system.stdout.writeLine("                LNHTTPRequest.reply() - sending complete output to client");
}

/**
 * Method: work
 *
 * Returns:
 *		0 - if request is malformed - "please close socket and client"
 *		1 - if request is okay and not multipart (not waiting for data), and executed already
 *		2 - needs to wait for multipart data
 *		3 - long polling
 */
LNHTTPRequest.prototype.work=function()
{
	var t=this;
	if (!this.request_method.match(/^(GET|POST|HEAD)$/)) this.errors.push("Unsupported method "+this.request_method);
	this.request_uri.replace(/^http:\/\/([^\/]+)/,function(p,p1) { t.request_headers.host=p1;return "";});
	if (!this.request_headers.host) this.errors.push("No host given");
	if (this.errors.length) {
		var msg='';
		msg+='<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n';
		msg+='<html><head>\n';
		msg+='<title>501 Errors found</title>\n';
		msg+='</head><body>\n';
		msg+='<h1>501 Errors found</h1>\n';
		msg+='<p>'+this.errors.join('</p>\n<p>')+'</p>\n';
		msg+='<hr>\n';
		msg+='<address>Lattenoir server</address>\n';
		msg+='</body></html>\n';
		this.reply("501 FAIL",msg);
		system.stdout.writeLine("\terrors:");
		system.stdout.write("\t"+this.errors.join("\n\t")+"\n\n");
		return 0;
	}
	if (this.request_headers["content-type"] && this.request_headers["content-type"].match(/^application\/x-www-form-urlencoded/)) {
		this.pending=true;
		return 2;
	}
	// return this.demo();
	return this.execute();
}

LNHTTPRequest.prototype.demo=function()
{
	var msg='';
	msg+='<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n';
	msg+='<html><head>\n';
	msg+='<title>200 OK</title>\n';
	msg+='</head><body>\n';
	msg+='<h1>200 test</h1>\n';
	for (var k in this.request_headers) {
		msg+='<p>'+k+': '+this.request_headers[k]+'</p>\n';
	}
	msg+='<hr>\n';
	msg+='<address>Lattenoir server</address>\n';
	msg+='</body></html>\n';
	this.reply("200 OK",msg);
	return 1;
}

LNHTTPRequest.prototype.execute=function(stdin)
{
	var t=this;
	system.env.REQUEST_METHOD	=(this.request_method=="HEAD")?"GET":this.request_method;
	system.env.SERVER_NAME		=this.request_headers["host"].replace(/:\d+$/,"");
	system.env.HTTP_USER_AGENT	=this.request_headers["user-agent"] || "Unknown";
	system.env.CONTENT_TYPE		=this.request_headers["content-type"];
	system.env.CONTENT_LENGTH	=this.request_headers["content-length"];
	system.env.REQUEST_URI		=this.request_uri;

	system.stdout.writeLine("request_method = '"+this.request_method+"'");
	system.stdout.writeLine("request_uri    = '"+this.request_uri+"'");
	system.stdout.writeLine("server_name    = '"+system.env.SERVER_NAME+"'");

	var msg;
	var stdinptr=0;
	var read	= function(bytes) {
		if (stdinptr>=stdin.length) return new Buffer(0);
		var ptr=stdinptr;
		stdinptr+=bytes;
		if (stdinptr>stdin.length) stdinptr=stdin.length;
		return new Buffer(stdin,ptr,stdinptr,false);
	};
	var write	=function(data) {
		system.stdout.writeLine("LNHTTPRequest.execute() - write() called, data.length="+data.length);
		if (!msg) {msg=data;return;}
		if (typeof msg=="string") {
			msg=new Buffer(msg,"UTF-8");
		}
		if (typeof data=="string") data=new Buffer(data,"UTF-8");
		var b=new Buffer(data.length+msg.length);
		b.copyFrom(msg ,0,0,msg.length);
		b.copyFrom(data,0,msg.length,msg.length+data.length);
		msg=b;
	}
	var header	=function(name, value) { t.reply_headers[name]=value; }
	var request	=new http.ServerRequest(read, system.env);
	var response=new http.ServerResponse(write, header);

	if (this.request_uri=="/comet/") {
		return this.execute_comet(header,request,response);
	} else {
		//return this.execute_app(header,request,response);
		this.app.http_work(request,response);
		this.reply(this.reply_headers["Status"]||"200 OK",msg);
		return 1;
	}
}
	
LNHTTPRequest.prototype.execute_comet=function(header,request,response)
{
	var fields=request[request.method.toLowerCase()];
	system.stdout.writeLine("    LNHTTPRequest.execute_comet()");
//	system.stdout.writeLine("      request_headers:");
//	for (var k in this.request_headers) system.stdout.writeLine("        "+k+" = "+this.request_headers[k]);
	system.stdout.writeLine("      GET/POST fields:");
	for (var k in fields) system.stdout.writeLine("        "+k+" = "+fields[k]);
	var ret;
	var deferred=false;
	switch (fields.mode) {
		case "subscribe":
			ret=this.comet.subscribe(this.socket,fields.clientid,fields.channelid);
			break;
		case "unsubscribe":
			ret=this.comet.unsubscribe(this.socket,fields.clientid,fields.channelid);
			break;
		case "create_id":
			ret=this.comet.create_id(this.socket);
			break;
		case "poll":
			this.comet.connect(this.socket,fields.clientid,this);
			deferred=true;
			break;
		case "notify":
			ret=this.comet.notify(this.socket,fields.channelid,fields.data,this);
			break;
	}
	if (!ret && !deferred) {
		ret=JSON.stringify({error:"Comet service did not provide an answer"});
	}
	if (!deferred) {
		this.reply_headers["Content-Type"]="application/json; charset=utf-8";
		this.reply(200,ret);
		return 1;
	} else {
		return 3;
	}
}

/*
LNHTTPRequest.prototype.execute_app=function(header,request,response)
{
	this.app.http_work(request,response);
	this.reply(this.reply_headers["Status"]||"200 OK",msg);
	return 1;
}*/

/**
 * Method: work_pending
 *		Waiting for second chunk of the data
 *
 * Returns:
 *		0 - some error, please disconnect
 *		1 - worked okay
 *		2 - still waiting
 *		3 - long polling
 */
LNHTTPRequest.prototype.work_pending=function(client)
{
	if (isNaN(this.request_headers["content-length"])) {
		system.stdout.writeLine("    LNHTTPRequest.work_pending() - error in content-length, please disconnect");
		return 0;
	}
	var len=this.request_headers["content-length"]*1;
	if (client.buffer.length<len) {
		system.stdout.writeLine("    LNHTTPRequest.work_pending() - not enough data");
		return 2;
	}
	var b=new Buffer(client.buffer,0,len,false);
	
	system.stdout.writeLine("    LNHTTPRequest.work_pending() - executing");
	var r=this.execute(b);

	var tmp=new Buffer(client.buffer,len,client.buffer.length);
	client.buffer=tmp;
	return r;
}


exports.LNHTTPRequest=LNHTTPRequest;
